"use strict"

define([ 'js/databinder' + $E.jsv,
	'js/objective' + $E.jsv,
	'js/customer' + $E.jsv ], function(DataBinder, Objective, Customer) {

	/**
	 * 系统顶级类：Instance，用于动态页面实例处理。
	 * @name Instance
	 * @class Instance
	 * @param project {object} 项目对象
	 * @param struct {object} 动态页面主结构
	 * @param [otId] {string} 市场活动ID
	 * @param [oId] {string} ObjecitveGUID
	 * @param [parameters] {any} 入参动态页面的局部变量
	 * @param [inner] {boolean} 是否作为内嵌独立组件使用，默认为false
	 * @param [type] {string} 动态页面类型，动态页面='DYN'，工单='WO'，客户资料='CUST'。默认为'DYN'
	 */
	var Instance = function(project, struct, otId, oId, parameters, inner, type) {
		var a = this;

		/**
		 * 动态页面实例所属的项目对象
		 * @property project
		 * @type {object}
		 */
		a.project = project;

		/**
		 * 实例ID
		 * @property id
		 * @type {string}
		 */
		a.id = Guid.raw();

		/**
		 * 实力类型，包括DYN,VIEW,PLUGIN,WO等
		 * @property type
		 * @type {string}
		 */
		a.type = type || 'DYN';

		/**
		 * 动态页面主结构数据
		 * @property struct
		 * @type {object}
		 */
		a.struct = struct;

		/**
		 * 市场活动ID
		 * @property otId
		 * @type {string}
		 */
		a.otId = otId;

		/**
		 * 客户业务数据ID，该参数仅当从非菜单点击事件创建实例是存在
		 * @property oId
		 * @type {string}
		 */
		a.oId = oId;

		/**
		 * 是否新创建了objective，该参数用于在打开TAB时判断是否重复
		 * @property flag
		 * @type {boolean}
		 */
		a.flag = false;

		/**
		 * 动态页面FAKE ID
		 * @property dynId
		 * @type {string}
		 */
		a.dynId = struct.id;
		a.dynGuid = struct.guid;

		/**
		 * 入参动态页面的局部变量
		 * @property parameters
		 * @type {any}
		 */
		a.parameters = parameters || {};

		/**
		 * 是否作为独立组件使用
		 * @property inner
		 * @type {boolean}
		 */
		a.inner = inner;

		/**
		 * 实例状态，0=无状态，1=有状态
		 * @property stateful
		 * @type {int}
		 */
		a.stateful = (struct.version == 'DYN2' ? struct.stateful : struct.SaveFlag);
		if (!a.stateful) {
			a.owner = a.parameters.OWNER;
			a.ownerInclusive = a.parameters.INCLUSIVE;
		}

		/**
		 * 定时器任务队列
		 */
		a.tasks = {};

		/**
		 * 实例内的消息管理器，可用于动态页面内部与内嵌插件的互动
		 */
		a.events = {
			unique : {}
		};

		/**
		 * 绑定客户资料对象的临时变量
		 */
		a.bindCustomerProperties = {};

		/**
		 * 绑定Objective对象的临时变量
		 */
		a.bindObjectiveProperties = {};

		/**
		 * SystemDo1请求队列，用于动态页面请求批量提交
		 * @property sysdos
		 * @type {map}
		 */
		a.sysdos = {};

		/**
		 * CacheDo1请求队列，用于动态页面请求批量提交
		 * @property cachedos
		 * @type {map}
		 */
		a.cachedos = {};

		/**
		 * Databinding请求队列，用于动态页面请求批量提交
		 * @property xmldos
		 * @type {map}
		 */
		a.xmldos = {};

		/**
		 * 实例数据
		 * @property data
		 * @type {object}
		 */
		a.data = {};

		/**
		 * SQL片段
		 */
		a.DSL = {};

		a.objective = undefined;

		/**
		 * 实例消息监听服务器
		 * @property databinder
		 * @type {object}
		 */
		a.databinder = DataBinder.create();

		/**
		 * @property externalListener
		 * @type {map}
		 * @private
		 */
		a.externalListener = {};

		/**
		 * @private
		 */
		a.attributes = {};

		/**
		 * 个性化数据
		 * @property storage
		 * @type {map}
		 * @private
		 */
		a.storage = {};

		/**
		 * 实例的运行环境
		 */
		a.runtime = {};

		/**
		 * 当前实例中的所有plugins对象
		 */
		a.plugins = [];

		/**
		 * 当前实例中打开的工单对象数组
		 */
		a.wos = [];
		/**
		 * 当前实例中内嵌的其他实例
		 */
		a.embed = [];

		a.customer = undefined;
		a.dbPool = '';

		/**
		 * 实例公共函数服务器
		 * @property dc
		 */
		if (struct.version === 'DYN2') {
			a.dc = new Executer(a, function(e, runtime) {
				a.batchFireCtl(e, runtime);
			})
		} else {
			a.dc = new E_Calc(a, function(e, runtime) {
				a.batchFireCtl(e, runtime);
			})
		}

		// 初始化实例公共变量
		a.data = a.initVars(struct, true);

		// 初始化运行时环境，根环境
		a.runtime = {
			id : a.struct.id,
			project : a.project,
			instance : a,
			struct : struct,
			afterDraw: [],
			props : {
				pub : (struct.version == 'DYN2' ? struct.props.pub : struct.VarList),
				local : (struct.version == 'DYN2' ? struct.props.local : struct.LocList)
			},
			data : {
				pub : a.data,
				local : a.initVars(struct)
			},
			// 页面流转步骤
			flows : [],
			// 绘制容器
			container : undefined,
			// 控件表
			ctls : {},
			// 变量监听器
			binder : {},
			sqlQueue : {},
			children : {},
			namespace : a.struct.id // 用于标示该运行环境所归属的页面，页面嵌套时，应该用'.'号进行串联标示
		// 改属性用于标示页面内的checkbox群、radio群、以及区别相同类型控件的scope
		};
		a.initStorage();
		return a;
	};

	Instance.prototype = {
		title : function(e) {
			var a = this;
			if (arguments.length > 0) {
				a.tabCtrl.find('span.title').text(e);
				return e;
			} else {
				return a.tabCtrl.find('span.title').text();
			}
		},

		/**
		 * 初始化实例数据默认值
		 *
		 * @memberOf Instance
		 * @param vars {object} 变量表
		 */
		initVars : function(struct, pub) {
			var a = this,
				data = {},
				vars,
				property;
			if (struct.version == 'DYN2') {
				vars = pub ? struct.props.pub : struct.props.local;
				for (var propertyName in vars) {
					property = vars[propertyName];
					data[property.ns] = data[property.ns] || {};
					data[property.ns][property.field] = a.getDefaultValue(property.t, property.v);
				}
			} else {
				vars = pub ? struct.VarList : struct.LocList;
				for (var propertyName in vars) {
					property = vars[propertyName];
					data[property.pM] = data[property.pM] || {};
					data[property.pM][property.pS] = a.getDefaultValue(property.VT, property.DV);
				}
			}
			return data;
		},

		/**
		 * 初始化私有数据
		 *
		 * @memberOf Instance
		 */
		initStorage : function() {
			var a = this;
			if (a.project.storage[a.struct.id]) {
				a.storage = a.project.storage[a.struct.id];
				return;
			}

			$F.dataService('SystemDo2', {
				data : $F.sqlbean('System.cache.get', a.project.token, [ $E.staff.id, a.runtime.namespace ]),
				digest : a.project.digest,
				async : false,
				success : function(data) {
					if (data.code < 0)
						$F.err('Instance - loadStorage', data.message);else {
						if (data.value[0].count < 0)
							$F.err('Instance - loadStorage', $F.lang('db.error') + data.value[0].key);
						else {
							if (data.value[0].count > 0) {
								$F.log('Instance loadStorage', 'find storage data [', data.value[0].count + ']');
								$.each(data.value[0].fields, function(i, v) {
									if (! a.storage[v[0]])
										a.storage[v[0]] = {};
									a.storage[v[0]][v[1]] = v[2];
								})
							}
							a.project.storage[a.struct.id] = a.storage
						}
					}
				}
			})
		},

		/**
		 * 初始化实例数据
		 *
		 * @method init
		 * @param objective {object} objective对象
		 * @param reuse {boolean} 是否重用instance
		 */
		init : function(objective, reuse = false) {
			var a = this;
			if (a.stateful) {
				// 当前实例具有保存状态，需要创建objective对象,并从服务器获取数据
				var objectiveState = false;
				if (objective) {
					// 已经存在objective对象
					a.objective = objective;
					a.oId = objective.getId();
					objectiveState = true;
				} else {
					// 没有objective，创建objective对象
					if (!a.project.getCustomerGuid())
						return $F.err('Instance - init', '有状态的动态页面实例不允许无框架客户');
					a.objective = Objective.create(a.project, '');
				}
				a.dbPool = a.objective.dbPool;

				if (a.struct.version === 'DYN2') {
					a.oId = a.objective.getId();
					// 加载现有的event
					a.objective.setupCurrentEvent();

					// load tabc
					var tabc = {}, vars = a.struct.props.pub;
					for (var propertyName in vars) {
						property = vars[propertyName];
						if (property.ns !== 'temp')
							tabc[property.ns] = 1;
					}
					tabc = Object.keys(tabc);
					if (tabc.length > 0) {
						var dataRequest = {
							token : a.project.token,
							dbPool : a.dbPool,
							sqls : []
						};
						tabc.forEach(function(name) {
							dataRequest.sqls.push({
								key : 'tabc.get',
								params : {
									C1 : name.toUpperCase(),
									C2 : $E.staff.epidSql,
									C3 : a.oId
								}
							})
						});
						$F.dataService('SystemDo2', {
							data : dataRequest,
							async : false,
							digest: a.project.digest,
							success : function(data) {
								if (data.code > 0) {
									data.value.forEach(function(value, i) {
										var name = tabc[i],
											bean = $F.makeJsonBean(value);
										if (bean) {
											for (var field in bean) {
												var prop = a.getProperty(name + '.' + field.toLowerCase(), a.runtime);
												if (prop && prop.t == 2) {
													if ($F.parseDate(bean[field]).getFullYear() === 1900)
														a.data[name][field.toLowerCase()] = '';
													else
														a.data[name][field.toLowerCase()] = bean[field];
												} else
													a.data[name][field.toLowerCase()] = bean[field];
											}
										}
									})
								} else
									$F.err('Instance - init', data.message)
							}
						})
					}
				}
				else {
					var makeInstanceData = function(data) {
						if (data.code > 0) {
							//console.log(data.value);
							if (!data.value.flag) {
								if (!objectiveState) {
									// 当加载实例数据记录失败时，创建新的objective对象数据，并且将实例的oId等同于id
									a.objective.createData(a.otId, a.id);
									a.flag = true;
								}
							}

							// 整理实例流程状态
							$.each(data.value.flow, function(i, v) {
								var page = $D.getPage(a.project, a.struct, v);
								if (!page) {
									$F.err('Instance - init', '页面数据加载失败：' + v);
									return false;
								}
								a.runtime.flows.push({
									id : page.id,
									name : page.name
								})
								a.runtime.page = page;
							});

							// 整理实例数据到objective
							$.each(data.value.data, function(i, v) {
								v.tableName = v.tableName.toLowerCase();
								v.fieldName = v.fieldName.toLowerCase();
								if (v.tableName.toUpperCase() === 'OBJ') {
									if (objectiveState)
										$F.err('Instance - init', '重复读取Objective数据');
									else {
										// objective数据不存在，需要加载
										var column = a.objective.fields[v.fieldName];
										if (!column)
											$F.err('Instance - init', 'Objective数据的字段名不可识别：' + v.fieldName);
										else
											a.objective.setOldValue(v.fieldName, v.value, column.type);
									}
								} else {
									// 动态页面数据
									// console.info('Load dyn data: ' + v.tableName + '.' + v.fieldName + '=' + v.value);
									if (!a.data[v.tableName])
										a.data[v.tableName] = {};

									var prop = a.getProperty(v.tableName + '.' + v.fieldName, a.runtime);
									if (prop && prop.VT == 2) {
										if ($F.parseDate(v.value).getFullYear() === 1900)
											a.data[v.tableName][v.fieldName] = '';
										else
											a.data[v.tableName][v.fieldName] = v.value;
									} else
										a.data[v.tableName][v.fieldName] = v.value;
								}
							});

							a.oId = a.objective.getId();
							//加载现有的event
							a.objective.setupCurrentEvent();
							if (!a.objective.wo3)
								a.objective.oblist.loadData(a.oId);
						} else
							$F.err('Instance - init', '动态页面实例数据加载失败：' + data.message);
					};
					// 从数据服务获取实例数据
					$F.logGroup('Instance - init', '读取动态页面数据和Objective数据');
					if (a.oId) {
						$F.dataService('JSGenerateServlet2', {
							data : {
								token : a.project.token,
								dbPool : a.dbPool,
								name : 'Dyn.LoadData.OBJGUID',
								cacheId : a.dynId,
								objGUID : a.oId,
								readObj : objectiveState ? 0 : 1,
								bakTB : ''
							},
							digest : a.project.digest,
							async : false,
							success : makeInstanceData
						})
					} else {
						$F.dataService('JSGenerateServlet2', {
							data : {
								token : a.project.token,
								dbPool : a.dbPool,
								name : 'Dyn.LoadData.OBJType',
								cacheId : a.dynId,
								objTypeId : a.otId,
								custGUID : a.project.getCustomerGuid()
							},
							digest : a.project.digest,
							async : false,
							success : makeInstanceData
						})
					}
				}
				// console.log('Objective data', a.objective);

				// 赋值实例的客户对象
				if ($F.equalsIgnoreCase(a.objective.getCustomerGuid(), a.project.getCustomerGuid())) {
					// 实例客户为当前框架客户
					a.customer = a.project.customer;
				} else {
					// 实例客户与当前框架客户不同
					a.customer = Customer.createByGuid(a.project, a.objective.getCustomerGuid(), a.dbPool);
				}
				$F.logGroupEnd();
			} else
				a.customer = a.project.customer;

			// 绑定特殊变量到Customer对象和Objective对象
			$F.log('Instance - init', '绑定特殊变量到Customer/Objective对象')
			var property,
				ns,
				field;
			for (var key in a.runtime.props.pub) {
				property = a.runtime.props.pub[key];
				ns = (a.struct.version === 'DYN2' ? property.ns : property.pM);
				if (ns === 'temp') {
					field = (a.struct.version === 'DYN2' ? property.field : property.pS);
					if (field.startWith('cust_')) {
						// 绑定到Customer对象
						a.bindCustomerProperties[key] = field.substring(5);
						// console.log('监听Customer：' + a.bindCustomerProperties[key]);
						if (a.customer) {
							a.data[ns] = a.data[ns] || {};
							a.data[ns][field] = a.customer.getValue(a.bindCustomerProperties[key])
						}
					} else if (field.startWith('obj_')) {
						// 绑定到Objective对象
						a.bindObjectiveProperties[key] = field.substring(4);
						// console.log('监听Objective：' + a.bindObjectiveProperties[key]);
						// 初始化该值
						if (a.objective) {
							a.data[ns] = a.data[ns] || {};
							a.data[ns][field] = a.objective.getValue(a.bindObjectiveProperties[key])
						}
					}
				}
			}
			a.bindCustomer(reuse);

			if (!reuse) {
				// 注册callstart时间监听
				a.project.events.register($CONST.CTIEvent.CTI_ONCALLSTART, a.id, function(callInfo) {
					if (a.objective) {
						var eventGuid = callInfo.getValue("event_guid");
						var callStartTime = callInfo.getValue("callStartTime");
						a.objective.newEvent(eventGuid, callStartTime);
					}
				});
			}

			$F.log("Instance - init", '创建' + (a.stateful ? '有' : '无') + '状态实例', ($E.jsv == '' ? a : a.id));
			return a;
		},

		bindCustomer : function(reuse = false) {
			if (!this.customer)
				return;

			var a = this;
			if (reuse) {
				a.project.events.remove(a.id, $CONST.ModuleEvent.ECM_CUSTOMERPROPERTIESCHANGED);
				a.attributes[$CONST.ModuleEvent.CUSTOMER_BINDED] = false;
			}
			if (a.customer == a.project.customer) {
				if (!a.attributes[$CONST.ModuleEvent.CUSTOMER_BINDED] && Object.keys(a.bindCustomerProperties).length > 0) {
					// 注册当前客户的属性发生改变时的消息
					a.attributes[$CONST.ModuleEvent.CUSTOMER_BINDED] = true;
					$F.log('Instance - bindCustomer', '注册客户资料关联监听');
					a.project.events.register($CONST.ModuleEvent.ECM_CUSTOMERPROPERTIESCHANGED, a.id, function(data) {
						var propertyName = 'temp.cust_' + data.field.toLowerCase();
						if (a.bindCustomerProperties[propertyName]) {
							if ($E.debug)
								console.log('[' + a.struct.id + ']检测到客户资料改变:' + $F.display(data));
							a.fireValue(propertyName, a.runtime, {
								type : 0,
								data : data.newValue,
								unbind: true
							}, {
								type: 'CUSTOMER',
								path: 'SYSTEM'
							});
						}
					})
				}
			} else {
				if (a.attributes[$CONST.ModuleEvent.CUSTOMER_BINDED]) {
					// 解除当前客户的属性发生改变时的消息
					a.attributes[$CONST.ModuleEvent.CUSTOMER_BINDED] = false;
					$F.log('Instance - bindCustomer', '解除客户资料关联监听');
					a.project.events.remove(a.id, $CONST.ModuleEvent.ECM_CUSTOMERPROPERTIESCHANGED);
				}
			}
		},

		unbindCustomer : function() {
			var a = this;
			if (a.customer == a.project.customer) {
				if (a.attributes[$CONST.ModuleEvent.CUSTOMER_BINDED]) {
					// 强制解除当前客户的属性发生改变时的消息
					$F.log('Instance - bindCustomer', '强制解除客户资料关联监听');
					a.project.events.remove(a.id, $CONST.ModuleEvent.ECM_CUSTOMERPROPERTIESCHANGED);
				}
				a.customer = a.customer.clone();
			}
		},

		/**
		 * 初始化命令组
		 * @param runtime
		 */
		initCmd : function(runtime) {
			var a = this;

			// console.log('初始化运行环境：' + runtime.id, runtime.struct)
			// 运行加载命令
			if (runtime.struct.version == 'DYN2') {
				var P0 = runtime.struct.cmds.internal.load;
				if (P0) {
					var mt = new Date();
					$F.logGroup('Instance - initCmd', '[' + runtime.id + ']开始执行加载命令');
					// console.log(runtime);
					a.dc.doCmdList('load@' + runtime.id, P0.items, '', $.extend({
						INSTANCE: this
					}, a.parameters), runtime);
					$F.logGroupEnd('Instance - initCmd', '加载命令耗时：' + (new Date() - mt) + '\'ms');
				}
			} else {
				var P0 = runtime.struct.CmdList['P0'];
				if (P0 && P0.length > 0) {
					var mt = new Date();
					$F.logGroup('Instance - initCmd', '[' + runtime.id + ']开始执行加载命令，共计：' + P0.length + ' 条');
					// console.log(runtime);
					a.dc.doCmdList('P0@' + runtime.namespace, P0, '', $.extend({
						INSTANCE: this
					}, a.parameters), runtime);
					$F.logGroupEnd('Instance - initCmd', '加载命令耗时：' + (new Date() - mt) + '\'ms');
				}
			}
		},

		/**
		 * 执行绘制前命令
		 */
		beforeDraw : function(runtime, currentPage) {
			var a = this;
			if (runtime.struct.version == 'DYN2') {
				currentPage = currentPage || runtime.page;
				var cmds = currentPage.cmds.before;
				if (cmds) {
					$F.logGroup('Instance - beforeDraw', '开始执行子页面渲染前命令');
					a.dc.doCmdList('page.before@' + runtime.struct.id + '.' + currentPage.id, cmds.items, '', {}, runtime);
					$F.logGroupEnd();
				}
			}
		},

		/**
		 * 执行页面的渲染后命令集
		 * @param runtime
		 */
		afterDraw : function(runtime, currentPage) {
			var a = this;
			currentPage = currentPage || runtime.page;
			if (runtime.struct.version == 'DYN2') {
				var cmds = currentPage.cmds.after;
				if (cmds) {
					a.dc.doCmdList('page.after@' + runtime.struct.id + '.' + currentPage.id, cmds.items, '', {}, runtime);
				}
				if (runtime.afterDraw) {
					$.each(runtime.afterDraw, function(i, fun) {
						fun.call(a);
					})
					runtime.afterDraw.length = 0;
				}
			} else {
				var cmdId = currentPage.cmdId;
				var cmds = runtime.struct.CmdList['P' + cmdId];
				if (cmds) {
					a.dc.doCmdList('P' + cmdId + '@' + runtime.struct.id, cmds, '', {}, runtime);
				}
			}
		},

		afterRender : function(runtime) {
			var a = this;
			runtime = runtime || this.runtime;

			// 注册框架客户切换时的消息回调
			a.project.events.register($CONST.ModuleEvent.ECM_CUSTOMERCHANGE, a.id, function() {
				if (runtime.struct.version == 'DYN2') {
					var cmds = runtime.struct.cmds.internal['customer.changed'];
					if (cmds) {
						$F.log('Instance', '开始执行变更客户命令');
						a.dc.doCmdList('customer.changed@' + runtime.id, cmds.items, '', {}, runtime);
					}
				} else {
					var cmds = runtime.struct.CmdList['P5'];
					if (cmds) {
						$F.log('Instance', '开始执行变更客户命令');
						a.dc.doCmdList('P5@' + runtime.id, cmds, '', {}, runtime);
					}
				}
			});

			if (runtime.struct.version == 'DYN2') {
				// 运行属性组监听器
				var props = runtime.struct.props,
					property,
					callback = function(propertyName, evt, runtime) {
						property = props.pub[propertyName] || props.local[propertyName];
						if (evt.id == undefined) {
							evt.id = Guid.raw();
						} else {
							if (property.eventKey == evt.id) {
								if ($E.debug)
									console.log('属性组监听器发现重复KEY，停止递传:' + evt.id);
								return;
							}
						}
						property.eventKey = evt.id;
						var cmd = runtime.struct.cmds.extend[property.e];
						if (cmd)
							a.dc.doCmdList(property.e + '@' + runtime.id, cmd.items, evt.id, $.extend({}, a.parameters), runtime);
					};
				for (var scope in props) {
					for (var key in props[scope]) {
						property = props[scope][key];
						if (property.e) {
							$F.log('Instance - afterRender', '变量监听器注册在：' + key);
							a.databinder.registerPg(key, runtime, {
								ctl : {
									id : 'PROGRP_' + property.ns + '_' + property.field,
									runtime : runtime
								},
								callback : callback
							})
							//if (property.dirty)
							callback(key, {}, runtime);
						}
					}
				}
			} else {
				// 运行属性组监听器
				if (runtime.struct.ProGrp) {
					var P0 = runtime.struct.ProGrp['P0'];
					if (P0 && P0.length > 0) {
						//a.dc.doCmdList('属性组命令集', P0, '', a.parameters, runtime);
						var pl = function(props, listener, i) {
							if (!props)
								return;
							$.each(props, function(i2, property) {
								$F.log('Instance - initCmd', '变量监听器注册在：' + property.PN);
								a.databinder.registerPg(property.PN, runtime, {
									ctl : {
										id : 'PROGRP_' + i + '_' + i2 + '_' + property.PN,
										runtime : runtime
									},
									callback : function(propertyName, evt, runtime) {
										// callback parameter
										if (evt.id == undefined) {
											evt.id = Guid.raw();
											listener.key = evt.id;
										} else {
											if (listener.key == evt.id) {
												if ($E.debug)
													console.log('属性组监听器发现重复KEY，停止递传:' + evt.id);
												return;
											}
											listener.key = evt.id;
										}
										a.dc.doCmdList('PROGRP_LISTENER', [ listener ], evt.id, $.extend({}, a.parameters), runtime);
									}
								})
								//if (a.getProperty(property.PN, runtime).dirty)
								a.dc.doCmdList('PROGRP_LISTENER', [ listener ], {}, $.extend({}, a.parameters), runtime);
							})
						}
						$.each(P0, function(i, listener) {
							pl(listener.RT, listener, i);
							pl(listener.RL, listener, i);
						})
					}
				}
			}

			runtime.status = 'ready';
		},

		registerEvent : function(eventKey, callback, runtime, scope) {
			var a = this;
			if (scope) {
				a.events[runtime.id] = a.events[runtime.id] || {};
				a.events[runtime.id][eventKey] = {
					callback : callback,
					runtime : runtime
				};
			} else {
				a.events.unique[eventKey] = {
					callback : callback,
					runtime : runtime
				};
			}
		},

		removeEvent : function(runtime, eventKey) {
			var a = this;
			if ($.type(runtime) == 'string') {
				eventKey = runtime;
				for (var id in a.events) {
					delete a.events[id][eventKey];
				}
			} else {
				if (a.events[runtime.id]) {
					if (eventKey)
						delete a.events[runtime.id][eventKey];
					else
						delete a.events[runtime.id];
				}
			}
		},

		fireEvent : function(eventKey, params) {
			var a = this,
				events = a.events,
				listener;
			for (var id in a.events) {
				listener = a.events[id][eventKey];
				if (listener) {
					if ($.isFunction(listener.callback))
						listener.callback(params, listener.runtime);
					else {
						$F.log('Instance - fireEvent', '触发实例消息事务：' + eventKey + ', cmd:' + listener.callback);
						var cmdId = listener.callback;
						a.doCmd(((listener.runtime.struct.version !== 'DYN2') ? ('P' + cmdId) : cmdId), params || {}, listener.runtime);
					}
				}
			}
		},

		/**
		 * 调用动态页面命令组，同时调用此页面组件中的命令组
		 * @param cmdId
		 * @param values
		 * @param runtime
		 * @param trigger
		 * @param internal
		 */
		doCmdIncludeComponents : function(cmdId, values, runtime, trigger, internal) {
			const a = this;
			const allComponents = CtlUtils.findAllComponents(this.runtime);
			if (cmdId === 'reset') { // 如果是页面重绘命令，则先执行页面命令组，再执行组件命令组
				a.doCmd(cmdId, values, runtime, trigger, internal);
				for (let key in allComponents) {
					allComponents[key].doCmd(cmdId, values, internal);
				}
			} else {
				for (let key in allComponents) {
					allComponents[key].doCmd(cmdId, values, internal);
				}
				a.doCmd(cmdId, values, runtime, trigger, internal);
			}
		},
		/**
		 * 调用动态页面指令集
		 * @method doCmd
		 * @param cmdId {string} 指令集序号
		 * @param [values] {object} 环境变量
		 * @param [runtime] {object} 运行环境，默认为顶层主结构环境
		 * @param [trigger] {string} 触发命令的使用者
		 * @return {map) 操作完成后的新环境变量
		 */
		doCmd : function(cmdId, values, runtime, trigger, internal) {
			runtime = runtime || this.runtime;
			if (runtime.struct.version == 'DYN2') {
				if (! runtime.struct)
					return values;
				var cmds = internal ? runtime.struct.cmds.internal[cmdId] : runtime.struct.cmds.extend[cmdId];
				//console.log(cmds);
				if (cmds)
					return this._doCmd(cmdId, cmds.items, values, runtime, trigger);
				else
					return values;
			} else
				return this._doCmd(cmdId, runtime.struct.CmdList[cmdId] || [], values, runtime, trigger);
		},

		/**
		 * 调用全局指令集
		 * for old version
		 * @method doPCmd
		 * @param {string} cmdId 指令集序号
		 * @param {map} [values] 环境变量
		 * @return {map) 操作完成后的新环境变量
		 */
		doPCmd : function(cmdId, values, runtime) {
			if (!runtime)
				runtime = this.runtime;
			return this._doCmd('G-' + cmdId, $D.getPCmd(runtime, cmdId) || [], values, runtime);
		},

		_doCmd : function(cmdId, cmds, values, runtime, trigger) {
			if (values) {
				values.INSTANCE = this;
				values.RUNTIME = runtime;
			} else {
				values = {
					INSTANCE : this,
					RUNTIME : runtime
				};
			}
			try {
				return this.dc.doCmdList(cmdId + '@' + runtime.namespace + (trigger ? ('.' + trigger) : ''), cmds, '', values, runtime);
			} catch (e) {
				$F.err('Instance - doCmd', e);
				return values;
			}
		},

		/**
		 * 获得一个变量对象
		 * @method getProperty
		 * @param dynId {string} 变量所在的主结构ID
		 * @param propertyName {string} 变量对象名称
		 * @return {object} 变量对象
		 */
		getProperty : function(propertyName, runtime) {
			if (!propertyName)
				return null;

			if (!runtime)
				runtime = this.runtime;
			propertyName = propertyName.toLowerCase();
			if (propertyName.startWith('var') || propertyName.startWith('tabc'))
				return runtime.props.local[propertyName];
			else
				return runtime.props.pub[propertyName];
		},

		/**
		 * 获得动态页面实例中的值
		 * @method getValue
		 * @param property {string | object} 变量对象名称或变量对象
		 * @param runtime {object} 运行环境
		 * @param notexist {any} 未定义变量时的替代值
		 * @return {any} 属性值
		 */
		getValue : function(propertyName, runtime, notexist) {
			runtime = runtime || this.runtime;
			var a = this,
				property = ($.type(propertyName) == 'string' ? a.getProperty(propertyName, runtime) : propertyName);
			if (property) {
				if (runtime.struct.version == 'DYN2') {
					var data = (property.ns == 'var' || property.ns == 'tabc') ? runtime.data.local : runtime.data.pub;
					// 获取实例数据
					if (data[property.ns] !== undefined && data[property.ns][property.field] !== undefined)
						return data[property.ns][property.field];
					else
						return a.getDefaultValue(property.t);
				} else {
					var data = (property.pM == 'var' || property.pM == 'tabc') ? runtime.data.local : runtime.data.pub;
					// 获取实例数据
					if (data[property.pM] !== undefined && data[property.pM][property.pS] !== undefined)
						return data[property.pM][property.pS];
					else
						return a.getDefaultValue(property.VT);
				}
			} else {
				if (notexist !== undefined)
					return notexist;
				else {
					$F.err('Instance - getValue', '变量未定义: ' + propertyName + '@' + runtime.struct.id);
					throw new Error('变量未定义: ' + propertyName + '@' + runtime.struct.id);
				}
			}
		},

		/**
		 * 根据变量名取有效值
		 * @method getValueByPropertyName
		 * @param runtime {object} 当前运行环境
		 * @param propertyName {string} 变量名
		 * @param data {any} 值
		 * @return 符合变量类型的有效值
		 */
		getValueByPropertyName : function(runtime, propertyName, data) {
			if (propertyName) {
				if (runtime.struct.version == 'DYN2')
					return this.getDefaultValue(this.getProperty(propertyName, runtime).t, data);
				else
					return this.getDefaultValue(this.getProperty(propertyName, runtime).VT, data);
			} else
				return data;
		},

		/**
		 * 根据值类型获取默认数据
		 * @method getDefaultValue
		 * @param type {int} 值属性对象
		 * @param [value] {any} 默认值
		 * @return {any} 格式化类型的值
		 */
		getDefaultValue : function(type, value) {
			switch (type) {
			case 1: // 数字
				if ($F.isNull(value))
					return 0;else {
					if (value === true)
						return 1;
					else if (value === false)
						return 0;
					else
						return $F.parseFloat(value);
				}
			case 2: // 日期
				if ($F.isNull(value)) {
					return '';
				} else if (value instanceof Date) {
					return value.format('yyyy-MM-dd HH:mm:ss');
				} else
					return value;
			case 3: // 字符串
				if ($F.isNull(value))
					return '';
				else
					return value.toString();
			case 4: // 数组
				if ($F.isNull(value) || value === '') return [];
				else {
					if ($.type(value) == 'array')
						return value;
					else if ($.type(value) == 'string') {
						try {
							return eval(value);
						} catch (e) {
							console.error(e);
							return [];
						}
					} else
						return [];
				}
			case 5: // 整数
				if ($F.isNull(value))
					return 0;else {
					if (value === true)
						return 1;
					else if (value === false)
						return 0;
					else
						return $F.parseInt(value);
				}
			case 6: // 对象
				if ($F.isNull(value))
					return {};
				else
					return value;
			default:
				$F.err('Instance - getDefaultValue', 'Undefined value type: ' + type);
				return '';
			}
		},

		/**
		 * 注册属性消息外部监听器
		 * @method propertyListener
		 * @param {string} listenerName 监听器名称
		 * @param {callback} callback 回调方法
		 *
		 * 	var callback = function(propertyName, value) {
		 * 	};
		 *	instance.externalListener('listenerName', callback);
		 *
		 * @return {instance} 实例对象
		 */
		propertyListener : function(listenerName, callback) {
			this.externalListener[listenerName] = callback;
		},

		/**
		 * 绑定变量监听，该方法用于绑定控件UI与变量的联动
		 * @method bindValue
		 * @param {string} propertyName 变量名称
		 * @param {object} ctl 控件对象
		 * @param {callback} callback 回调函数
		 * @param {boolean} trigger 是否立即触发
		 *
		 *	function callback(evt) {
		 *		// evt对象参考fireValue方法中的evt对象说明
		 *	}
		 *
		 */
		bindValue : function(propertyName, ctl, callback, trigger) {
			// 绑定变量变动消息
			if (propertyName == undefined)
				return;

			if (propertyName.indexOf('.') !== -1) {
				var property = this.getProperty(propertyName, ctl.runtime);
				if (property) {
					this.databinder.register(propertyName, ctl.runtime, {
						ctl : ctl,
						callback : callback
					});
					if (trigger)
						callback({
							type : 0,
							data : this.getValue(propertyName, ctl.runtime)
						})
				} else {
					$F.err('Instance - bindValue', '控件绑定变量失败：[' + propertyName + '@' + ctl.path + ']');
				}
			}
		},

		/**
		 * 批量执行对实例中的属性进行赋值，并且通知所有监听该属性的控件对象
		 * @method batchFireValue
		 * @param evts {object} 消息通知列表
		 * @param force {boolean} 当变量未申明时是否依然强制赋值
		 * @param runtime {object} 运行环境，不传递时就以当前runtime为准
		 *	evts: {
		 *	   propertyName:  		// 属性名称
		 *	   {
		 *		  type: 0/1,		// 消息类型, 0：data为一般数据，1：data为二维表格数据
		 *		  data: {any}		// 消息数据
		 *	   },
		 *		...
		 *	}
		 *
		 * @return instance 实例化对象
		 */
		batchFireValue : function(evts, force, runtime) {
			var a = this,
				evt,
				property,
				runtime = runtime || a.runtime;
			for (var propertyName in evts) {
				a.fireValue(propertyName, runtime, evts[propertyName], force);
			}
			return this;
		},

		/**
		 * 对实例中的属性值进行改变，并且通知所有监听该属性的控件对象
		 * @method fireValue
		 * @param propertyName {string} 变量名称
		 * @param runtime {object} 环境变量
		 * @param e {object} 值消息
		 *
		 * 	e : {
		 *		type: {boolean} 是否为表格属性专用的二维数组
		 *		data: {any} 普通值或二维数组
		 *		[
		 * 			{
		 * 				r: {integer} 行,
		 * 				c: {integer} 列,
		 * 				v: {any} 值
		 * 			}
		 * 			...
		 * 		]
		 * 	}
		 *
		 * @param ctl {object} 发送消息的源控件
		 * @param force {boolean} 变量未申明时是否依然强制赋值
		 * @return {boolean} 实例中的变量是否被实际改变
		 */
		fireValue : function(propertyName, runtime, e, ctl, force) {
			var a = this;
			if ($.type(ctl) === 'boolean') {
				force = ctl;
				ctl = false;
			}
			if (a.setValue(propertyName, runtime, e, force)) {
				if ($E.debug)
					console.log('值[' + propertyName + ']发生改变' + (ctl ? (', 来源[' + ctl.type + ']:' + ctl.path) : ''), e.data);
				a.databinder.fireCtl(propertyName, runtime, e, ctl);

				//					$.each(a.components, function(i, comp) {
				//						a.databinder.fireCtl(propertyName, comp.runtime, e, ctl, true);
				//					})
				a.sqlexec(runtime);
				return true;
			}
			return false;
		},

		/**
		 * 对实例中的属性值进行改变
		 *
		 * @method setValue
		 * @param propertyName {string} 变量名称
		 * @param runtime {object} 运行环境
		 * @param e {object} 值消息
		 * @param force {boolean} 变量未申明时是否依然强制赋值
		 * @return {boolean} 实例中的变量是否被实际改变
		 * @private
		 */
		setValue : function(propertyName, runtime, e, force) {
			var a = this, property = a.getProperty(propertyName, runtime);
			if (property) {
				property.dirty = true;
				if (runtime.struct.version == 'DYN2') {
					var data = (property.ns == 'var' || property.ns == 'tabc') ? runtime.data.local : runtime.data.pub;
					data[property.ns] = data[property.ns] || {};
					if (e.type == 2) {
						// 对象变量
						if (data[property.ns][property.field][e.key] !== e.data) {
							data[property.ns][property.field][e.key] = e.data;
							return true;
						}
					} else if (e.type == 1) {
						// 二维数组
						var array = a.getValue(propertyName, runtime), ks;
						for (var key in e.data) {
							ks = key.split(',');
							if (ks.length == 1) {
								// 整行更新
								if (key == '-1') {
									// 添加行
									array.push(e.data[key]);
								} else
									array[key] = e.data[key];
							} else {
								// 单元更新
								if (!array[ks[0]]) {
									$F.err('Instance - setValue', '表格控件变量[' + propertyName + ']的行数据不存在，行号:' + ks[0]);
								// console.log(data[property.ns][property.field])
								} else
									array[ks[0]][ks[1]] = e.data[key];
							}
						}
						return true;
					} else {
						// 普通值
						e.data = a.getDefaultValue(property.t, e.data);
						if (data[property.ns][property.field] !== e.data || property.t == 4 || property.t == 6) {
							// 数组变量可能会发生同一个数据指针相等，但是记录内容不一致的情况
							data[property.ns][property.field] = e.data;
							if (property.ns !== 'var' && property.field !== 'tabc' && property.t !== 4 && property.t !== 6) {
								// 公共变量需要进行customer和objective双向绑定
								a.syncSystemObject(propertyName, e.data);
							}
							return true;
						}
					}
				} else {
					var data = (property.pM == 'var' || property.pM == 'tabc') ? runtime.data.local : runtime.data.pub;
					data[property.pM] = data[property.pM] || {};
					if (e.type == 2) {
						// 对象变量
						if (data[property.ns][property.field][e.key] !== e.data) {
							data[property.ns][property.field][e.key] = e.data;
							return true;
						}
					} else if (e.type) {
						// 二维数组
						var array = a.getValue(propertyName, runtime),
							ks;
						for (var key in e.data) {
							ks = key.split(',');
							if (ks.length == 1) {
								// 整行更新
								if (key == '-1') {
									// 添加行
									array.push(e.data[key]);
								} else
									array[key] = e.data[key];
							} else {
								// 单元更新
								if (!array[ks[0]]) {
									$F.err('Instance - setValue', '表格控件变量[' + propertyName + ']的行数据不存在，行号:' + ks[0]);
								// console.log(data[property.pM][property.pS])
								}
								else
									array[ks[0]][ks[1]] = e.data[key];
							}
						}
						return true;
					} else {
						// 普通值
						e.data = a.getDefaultValue(property.VT, e.data);
						if (data[property.pM][property.pS] !== e.data || property.VT == 4 || property.VT == 6) {
							console.log('old value = ' + data[property.pM][property.pS] + ', new value = ' + e.data)
							// 对象型变量可能会发生同一个数据指针相等，但是记录内容不一致的情况
							data[property.pM][property.pS] = e.data;
							if (property.pM !== 'var' && property.pM !== 'tabc') {
								// 公共变量需要进行customer和objective双向绑定
								a.syncSystemObject(propertyName, e.data);
							}
							return true;
						}
					}
				}
			} else {
				if (force) {
					// only for VE
					var cs = propertyName.split('.'), ns = cs[0].toLowerCase(), field = cs[1].toLowerCase();
					var data = (ns == 'var' || ns == 'tabc') ? runtime.data.local : runtime.data.pub;
					data[ns] = data[ns] || {};
					if (e.type) {
						// 不支持对为申明的数组变量进行行列数据赋值
					} else {
						data[ns][field] = e.data
					}
				} else
					$F.err('Instance - setValue', '变量未定义: ' + propertyName + ' @' + runtime.struct.id);
			}
			return false;
		},

		/**
		 * 对系统内部对象进行同步
		 * @method syncSystemObject
		 * @param propertyName {string} 变量名称
		 * @param data {any} 变量数据
		 */
		syncSystemObject : function(propertyName, data) {
			propertyName = propertyName.toLowerCase();
			var a = this,
				objField = a.bindObjectiveProperties[propertyName],
				custField = a.bindCustomerProperties[propertyName];
			if (objField) {
				// 检查该变量是否绑定到objective
				if (a.objective && a.objective.getValue(objField) !== data) {
					$F.log(a.struct.id +' - syncSystemObject', 'OBJECTIVE对象属性[' + objField + ']发生变化，自动同步为：' + data);
					a.objective.setValue(objField, data);
				}
			} else if (custField) {
				// 检查该变量是否绑定到customer
				if (a.customer && a.customer.getValue(custField) !== data && !$F.equalsIgnoreCase(custField, 'customer_guid')) {
					$F.log(a.struct.id +' - syncSystemObject', 'CUSTOMER对象属性[' + custField + ']发生变化，自动同步为：' + data);
					a.customer.setValue(custField, data);
				}
			}
		},

		/**
		 * 批量执行值属性变更后的消息通知
		 * @method batchFireCtl
		 * @param evts {object} 消息通知列表
		 * @param runtime {object} 运行时
		 *
		 *	evts: {
		 *		propertyName:
		 *		{
		 *			type: 0/1,		// 消息类型, 0：data为一般数据，1：data为二维表格数据
		 *			data: {any}		// 消息数据
		 *		},
		 *		...
		 *	}
		 *
		 * @return instance 实例化对象
		 */
		batchFireCtl : function(evts, runtime) {
			var a = this,
				key = evts['#'],
				evt;
			if (key) {
				// 用于属性组监听器内部消息递归的终止标示
				if ($E.debug)
					console.log("属性对象监听器内部传递：" + key);
			}
			for (var propertyName in evts) {
				if (propertyName == '#') // 终止符，忽略
					continue;

				evt = evts[propertyName];
				if (key)
					evt.id = key;

				if (!runtime && (propertyName.startWith('var') || propertyName.startWith('tabc')))
					throw new Error('[batchFireCtl]私有变量操作时缺少运行环境');
				a.databinder.fireCtl(propertyName, runtime, evt);
			}
			$.each(a.databinder.runtimes, function() {
				a.sqlexec(this);
			})
			return this;
		},


		/**
		 * 创建一个sql查询队列对象
		 * @method newSqlQueue
		 * @param id {string} 队列的GUID
		 * @return {object} sql队列对象
		 */
		newSqlQueue : function(id) {
			return {
				id : id,
				queue : {}
			}
		},

		/**
		 * 批处理sql，通常用于选项等的关联sql
		 * @method executeSqls
		 * @param {object} sqlQueue sql查询队列对象
		 * @return instance 实例化对象
		 */
		sqlexec : function(runtime) {
			// console.log('执行实例[' + runtime.id + ']动态SQL：' + Object.keys(runtime.sqlQueue).length + '条')
			//递归执行所有子runtime中的sqlQueue
			for (var childKey in runtime.children) {
				this.sqlexec(runtime.children[childKey]);
			}
			if (Object.keys(runtime.sqlQueue).length == 0)
				return this;

			var a = this,
				sqls = [],
				sqlQueue = $.extend({}, runtime.sqlQueue);
			runtime.sqlQueue = {};

			/*
			 * 获得执行结果后，调用相应的callback，并设置缓存
			 */
			if (runtime.struct.version == 'DYN2') {
				for (var key in sqlQueue) {
					if (key == 'version')
						continue;
					sqls.push({
						sqlId : key,
						params : sqlQueue[key].params
					});
				}
				$F.dataService('Dyn2CahceDo2', {
					data : {
						token : a.project.token,
						dbPool : a.dbPool,
						dynId : runtime.struct.guid,
						sqls : sqls
					},
					digest : a.project.digest,
					async : false,
					success : function(data) {
						// console.log(data);
						if (data.code > 0) {
							$.each(data.value, function(i, v) {
								//console.log(v.key);
								try {
									if ($.isFunction(sqlQueue[v.key].callback))
										sqlQueue[v.key].callback(v);
									else
										$F.err('Instance - sqlexec', 'Has no callback function defined, SQLKEY=' + v.key);
								} catch (e) {
									console.error(e)
								}
							});
						} else {
							$F.err('Instance - sqlexec', data.message)
						}
					}
				});
			} else {
				for (var key in sqlQueue) {
					if (key == 'version')
						continue;
					sqls.push({
						cacheId : sqlQueue[key].structId,
						sqlId : key,
						para : sqlQueue[key].params
					});
				}
				$F.dataService('CacheDo2', {
					data : {
						token : a.project.token,
						dbPool : a.dbPool,
						sqls : sqls
					},
					digest : a.project.digest,
					async : false,
					success : function(data) {
						// console.log(data);
						if (data.code > 0) {
							$.each(data.value, function(i, v) {
								//console.log(v.key);
								if ($.isFunction(sqlQueue[v.key].callback))
									sqlQueue[v.key].callback(v);
								else
									$F.err('Instance - sqlexec', 'Has no callback function defined, SQLKEY=' + v.key);
							});
						} else {
							if ($.isFunction(sqlQueue[v.key].error))
								sqlQueue[v.key].error(data.message);
							else
								$F.err('Instance - sqlexec', data.message)
						}
					}
				});
			}
			return this;
		},

		getSQLValues : function(sqlKey, runtime) {
			return this.dc.getMDLPara(sqlKey, runtime);
		},

		ognl : function(string, runtime) {
			if (!string)
				return '';
			var a = this,
				ex,
				k,
				matches = string.match(/\$\{([\u4e00-\u9fa5 | \w | \.]+)\}/gi);
			runtime = runtime || a.runtime;
			if (matches) {
				for (var i = 0; i < matches.length; i++) {
					string = string.replace(matches[i], function() {
						var k = /\$\{([\u4e00-\u9fa5 | \w | \.]+)\}/gi.exec(matches[i])[1].toLowerCase();
						if (k.indexOf('.') > -1) {
							// 普通变量
							return a.getValue(k.toLowerCase(), runtime);
						} else {
							// 静态变量
							return a.getStaticVar(k, runtime);
						}
					});
				}
			}
			return string;
		},

		getStaticVar : function(propertyName, runtime) {
			propertyName = propertyName.toUpperCase();
			var cwInfo = runtime.project.cwInfo,
				instance = runtime.instance,
				project = runtime.project,
				struct = runtime.struct;
			try {
				switch(propertyName) {
				case 'C_GUID':
					return Guid.raw();
				case 'C_NOW':
					return $F.getDbTime();
				case 'DYNGUID':
					return struct.guid;
				case 'DYNID':
					return struct.id;
				case 'EPID':
				case 'C_EPID':
					return $E.staff.epid;
				case 'EPIDTB':
				case 'C_EPIDTB':
					return $E.staff.epidSql;
				case 'STAFFID':
				case 'C_BY':
					return $E.staff.id;
				case 'STAFFNAME':
					return $E.staff.name;
				case 'AGENTID':
					return $E.staff.agentId;
				case 'ROLEGROUPID':
				case 'C_GRP':
					return project.groupId;
				case 'ROLEGROUPNAME':
					return project.groupName;
				case 'ANI':
				case 'DNIS':
				case 'EXTENSION':
					if (cwInfo && cwInfo.callInfo && cwInfo.callInfo.getValue) {
						return cwInfo.callInfo.getValue(propertyName.toLowerCase()) || '';
					} else
						return '';
				case 'CALLTYPE':
					if (cwInfo)
						return cwInfo.callType || '';
					else
						return '';
				case 'CUREVENTID':
					if (cwInfo)
						return cwInfo.eventGuid || '';
					else
						return '';
				case 'G_ISSUPER':
					var isSupervisor = parseInt(project.getParam('ISSUPE', 2));
					return ($E.staff.supervisor > isSupervisor) ? 1 : 0;
				case 'G_SFASHA':
					return parseInt(project.getParam('SFASHA', 0));
				default:
					if (propertyName.startWith("OBJ_")) {
						propertyName = propertyName.substring(4);
						var propertyValue = instance.objective.getValue(propertyName);
						if (propertyValue === null || propertyValue === undefined) {
							return '';
						}
						return propertyValue;
					} else if (propertyName.startWith("CUST_")) {
						propertyName = propertyName.substring(5);
						var propertyValue = instance.customer.getValue(propertyName)
						if (propertyValue === null || propertyValue === undefined) {
							return '';
						}
						return propertyValue;
					}
				}
			} catch (e) {
				$F.err("Instance - getStaticVar", "Get [" + propertyName + "] failed", e);
			}
		},


		/**
		 * 置入一个定时任务
		 * @method putTask
		 * @param runtime {object} 动态页面运行时
		 * @param name {string} 定时器名称
		 * @param delay {integer} 延时执行秒
		 * @param immediate {boolean} 是否立即执行
		 * @param cmdId {string} 任务回调的命令组ID
		 * @param ev {any} 任务执行入参环境
		 * @param [useWorker] {boolean} 是否使用web worker（保证定时任务的执行）
		 * @param [pauseOnInactive] {boolean} 在inactive状态下是否需要暂停
		 * @return {boolean}
		 */
		putTask : function(runtime, name, delay, immediate, cmdId, ev, useWorker, pauseOnInactive) {
			if (arguments.length < 5) {
				$F.err('Instance - putTask', '定时器命令格式错误，缺少命令号参数');
				return false;
			}
			const a = this;
			if (a.tasks[name]) {
				$F.err('Instance - putTask', '相同名称的定时器已经在实例内运行，命令号：' + a.tasks[name].cmdId);
				return false;
			}
			const task = {
				name,
				cmdId : cmdId,
				times : 0,
				runtime,
				delay,
				immediate,
				ev,
				useWorker,
				pauseOnInactive
			};
			a.tasks[name] = task;
			a.runTask(task);
			return true;
		},

		runTask: function(task) {
			const a = this;
			const {name, runtime, delay, cmdId, ev, useWorker, immediate, pauseOnInactive} = task;
			if (useWorker) {
				a.tasks[name].worker = new Worker($E.url + '/js/cmdlib/task.worker' + $E.jsv + '.js');
				a.tasks[name].worker.addEventListener('message', function() {
					proc(runtime, name, delay, cmdId, ev);
				});
			}

			var proc = function(runtime, name, delay, cmdId, ev) {
				try {
					if (!a.tasks[name]) {
						return;
					}
					ev = ev || {};
					if (! ev.TASK)
						ev.TASK = {};
					ev.TASK.NAME = name;
					ev.TASK.STATE = true;
					if (pauseOnInactive && !a.isActive) {
						// $F.log('Instance - runTask', 'Instance inactive, pausing task: ' + name);
					} else {
						$F.log('Instance - runTask', '执行定时器: ' + name + ', delay=' + delay + ', cmdId=' + cmdId.toString());
						a.tasks[name].times++;
						ev.TASK.TIMES = a.tasks[name].times;
						try {
							if ($.isFunction(cmdId))
								cmdId(ev, runtime);
							else {
								a.doCmd(((runtime.struct.version !== 'DYN2') ? ('P' + cmdId) : cmdId), ev, runtime);
							}
						} catch (e) {
							$F.err('Instance - runTask.doCmd', e);
						}
					}

					if (ev.TASK.STATE && a.tasks[name]) {
						if (a.tasks[name].worker) {
							a.tasks[name].worker.postMessage({
								cmd: 'task',
								timeout: delay * 1000
							})
						} else {
							a.tasks[name].handle = setTimeout(function() {
								proc(runtime, name, delay, cmdId, ev);
							}, delay * 1000);
						}
					} else {
						delete a.tasks[name];
						$F.log('Instance - runTask', '定时器已终止: ' + ev.TASK.NAME);
					}
				} catch (e) {
					$F.err('Instance - runTask', e);
				}
			};
			if (immediate) {
				// 立即执行定时器
				proc(runtime, name, delay, cmdId, ev);
			} else {
				if (a.tasks[name].worker) {
					a.tasks[name].worker.postMessage({
						cmd: 'task',
						timeout: delay * 1000
					})
				} else {
					a.tasks[name].handle = setTimeout(function() {
						proc(runtime, name, delay, cmdId, ev);
					}, delay * 1000);
				}
			}
		},

		clearTask : function(name) {
			if (this.tasks[name]) {
				if (this.tasks[name].handle) {
					clearTimeout(this.tasks[name].handle);
				}
				if (this.tasks[name].worker) {
					this.tasks[name].worker.terminate();
				}
				delete this.tasks[name];
				return true;
			}
			return false;
		},

		clearAllTasks : function() {
			for (let key in this.tasks) {
				if (this.tasks[key]) {
					if (this.tasks[key].handle) {
						clearTimeout(this.tasks[key].handle);
					}
					if (this.tasks[key].worker) {
						this.tasks[key].worker.terminate();
					}
				}
			}
			if (Object.keys(this.tasks).length > 0)
				$F.log('Instance - destroy', '清除所有定时事务');
			this.tasks = {};
		},

		/**
		 * 向Objective对象写入一个值
		 * @method setObjectiveValue
		 * @param field {string} 名
		 * @param value {any} 值
		 */
		setObjectiveValue : function(field, value) {
			if (this.objective)
				this.objective.setValue(field, value);
		},

		/**
		 * 获取Objective对象的一个值
		 * @method getObjectiveValue
		 * @param field {string} 名
		 * @return {any} 值
		 */
		getObjectiveValue : function(field) {
			if (this.objective)
				return this.objective.getValue(field);
			return null;
		},

		/**
		 * 向Objective中的OB对象写入一个值
		 * @method setObjectiveObValue
		 * @param field {string} 名
		 * @param value {any} 值
		 */
		setObjectiveObValue : function(filed, value) {
			if (this.objective)
				this.objective.setObListValue(field, value);
		},

		setAttribute : function(key, value) {
			this.attributes[key] = value;
		},

		getAttribute : function(key) {
			return this.attributes[key];
		},

		removeAttribute : function(key) {
			delete this.attributes[key];
		},

		showWOFlowNode : function(node, e) {
			if (this.woInfoLayer) {
				// 实例被指定了用于浮动的层
				var layer = this.runtime.navBar.data('info');
				if (!this.woInfoLayer.init) {
					this.woInfoLayer.init = true;
					layer.append(this.woInfoLayer.dom);
					this.woInfoLayer.state(9);
				}
				H5Utils.drift(e.offset().top, e.offset().left, e.offset().top + e.outerHeight(), e.offset().left + e.outerWidth(), layer);
			}
			this.doCmdIncludeComponents('P-13', {
				NODE : node
			}, this.runtime);
		//console.log(node)
		},

		setHisGuid : function(guid) {
			// 自动生成HisGuid字段
			var data = this.data
			$.each(Object.keys(data), function(i, ns) {
				if (ns !== 'temp')
					data[ns].hisguid = guid;
			})
		},

		/**
		 * 增加一个批量提交的SystemDo1接口请求
		 * @method sysdoRequest
		 * @param {string} dbPool 连接池
		 * @param {array} sqls SQL请求的内容，每一项都包括sqlKey, params
		 *
		 * sqls:[
		 * 	{
		 * 		key: {string} SQL主键,
		 * 		params: {map} SQL请求的参数
		 *  }
		 *  ...
		 * ]
		 *
		 * @return {string} 该请求置入实例批量化队列中的主键
		 */
		sysdoRequest : function(dbPool, sqls) {
			var key = Guid.raw();
			this.sysdos[key] = {
				dbPool : dbPool,
				sqls : sqls
			}
			return key;
		},

		/**
		 * 移除一个已经提交的SystemDo1接口请求
		 * @method sysdoRemove
		 * @param {string} key SystemDo1接口请求主键
		 * @return {boolean} 移除是否成功
		 */
		sysdoRemove : function(key) {
			if (this.sysdos[key])
				return this.sysdos[key] = undefined, true;
			return false;
		},

		/**
		 * 增加一个批量提交的CacheDo1接口请求
		 * @method cachedosRequest
		 * @param {string} dbPool 连接池
		 * @param {array} sqls SQL请求的内容，每一项都包括cacheId, sqlKey, params
		 *
		 * sqls:[
		 * 	{
		 * 		key: {string} SQL主键,
		 * 		params: {map} SQL请求的参数
		 *  }
		 *  ...
		 * ]
		 *
		 * @return {string} 该请求置入实例批量化队列中的主键
		 */
		cachedosRequest : function(dbPool, sqls) {
			var key = Guid.raw();
			this.cachedos[key] = {
				dbPool : dbPool,
				sqls : sqls
			}
			return key;
		},

		/**
		 * 移除一个已经提交的CacheDo1接口请求
		 * @method cachedosRemove
		 * @param {string} key CacheDo1接口请求主键
		 * @return {boolean} 移除是否成功
		 */
		cachedosRemove : function(key) {
			if (this.cachedos[key])
				return this.cachedos[key] = undefined, true;
			return false;
		},

		/**
		 * 增加一个批量提交的Databinding接口请求
		 * @method xmldosRequest
		 * @param {string} dbPool 连接池
		 * @param {string} xmlData binding的内容
		 * @return {string} 该请求置入实例批量化队列中的主键
		 */
		xmldosRequest : function(dbPool, xmlData) {
			var key = Guid.raw();
			this.xmldos[key] = {
				dbPool : dbPool,
				xmlD : xmlData
			}
			return key;
		},

		/**
		 * 移除一个已经提交的Databinding接口请求
		 * @method xmldosRemove
		 * @param {string} key Databinder接口请求主键
		 * @return {boolean} 移除是否成功
		 */
		xmldosRemove : function(key) {
			if (this.xmldos[key])
				return this.xmldos[key] = undefined, true;
			return false;
		},

		/**
		 * 移除所有已经提交的Databinding接口请求
		 */
		xmldosRemoveAll : function() {
			this.xmldos = {};
		},

		saveIgnore : function(tabcName) {
			var a = this,
				ignore = a.attributes['__IGNORE_TABC_SAVE'] || [];
			if (tabcName) {
				if (tabcName instanceof Array) {
					tabcName.forEach(function(v) {
						if (!ignore.contains(v))
							ignore.push(v);
					})
				} else {
					if (!ignore.contains(tabcName))
						ignore.push(tabcName);
				}
				a.attributes['__IGNORE_TABC_SAVE'] = ignore;
			}
		},

		clearIgnore : function(tabcName) {
			var a = this,
				ignore = a.attributes['__IGNORE_TABC_SAVE'] || [];
			if (tabcName) {
				if (tabcName instanceof Array) {
					tabcName.forEach(function(v) {
						ignore.remove(v)
					})
				} else {
					ignore.remove(tabcName);
				}
				a.attributes['__IGNORE_TABC_SAVE'] = ignore;
			}
		},

		/**
		 * 对实例进行保存
		 * @method save
		 * @return {boolean} 保存是否成功
		 */
		save : function() {
			var a = this;
			// 执行所有虚拟组件的保存前命令
			var allComponents = CtlUtils.findAllComponents(this.runtime);
			for (var key in allComponents) {
				allComponents[key].beforeSave();
			}
			// 执行动态页面保存前命令
			var R = {}, cmdId = ((a.struct.version == 'DYN2') ? 'save.before' : 'P-10');
			a.doCmd(cmdId, R, a.runtime, '', true);
			R = R['RESULT'];
			if (R !== undefined && R < 1)
				return 0;

			// 调用组件的save接口
			for (var key in allComponents) {
				allComponents[key].save();
			}

			var saveFlag = false;
			if (a.struct.version == 'DYN2') {
				saveFlag = a.saveDyn2();
			} else {
				saveFlag = a.saveLegacy();
			}

			if (saveFlag) {
				// 执行所有虚拟组件的保存后命令
				for (var key in allComponents) {
					allComponents[key].afterSave();
				}
				var cmdId = ((a.struct.version == 'DYN2') ? 'save.after' : 'P1');
				a.doCmd(cmdId, {}, a.runtime, '', true);

				var callend = a.getAttribute($CONST.CTIEvent.CTI_AUTOCALLENDAFTERSAVE);
				if (callend !== undefined) {
					// 保存完成后，判断是否需要自动小结
					$PublicFun.autoCallEnd(a.project, callend.showFlag, callend.showNext);
				}
			}
			return saveFlag;
		},

		saveLegacy : function() {
			var a = this;
			// 整理数据上传对象
			var i = 0,
				key,
				upload = {},
				dbPool = a.dbPool;

			// 整理flow路径
			var dynFlows = [];
			if (a.runtime.flows.length > 1) {
				for (i = 0; i < a.runtime.flows.length; i++) {
					dynFlows.push(a.runtime.flows[i].id);
				}
			}

			// 整理data，剔除临时变量temp.xxx
			var newData = {}, ignore = a.attributes['__IGNORE_TABC_SAVE'] || [];
			for (key in a.data) {
				if (key == 'temp' || ignore.contains(key))
					continue;
				newData[key] = a.data[key];
			}
			newData['Objective'] = {
				OBJECTIVE_GUID : a.oId
			};
			upload.dyns = {
				dbPool : dbPool,
				buildId : a.dynId,
				path : dynFlows,
				data : newData
			};

			// 整理sysdos
			upload.sys = [];
			for (key in a.sysdos) {
				a.sysdos[key] && upload.sys.push(a.sysdos[key]);
			}

			// 整理cachedos
			upload.caches = [];
			for (key in a.cachedos) {
				a.cachedos[key] && upload.caches.push(a.cachedos[key]);
			}

			// 整理xmldos
			upload.xmls = [];
			for (key in a.xmldos) {
				a.xmldos[key] && upload.xmls.push(a.xmldos[key]);
			}
			var ox = a.objective.getXmlData();
			if (ox) {
				upload.xmls.push({
					dbPool : dbPool,
					xmlD : ox
				});
			}

			// 框架不再自动保存客户，要保存需要自己去调用$instance.customer.save();
			// 整理客户资料对象
			//				if (a.customer.getValue('customer_guid') != a.project.customer.getValue('customer_guid')) {
			//					var changed = a.customer.getChangedProperties();
			//					if (changed.length > 0) {
			//						var bd = new BoundData("Customer" + $E.staff.epidSql);
			//						bd.KeyInfoEdit('customer_guid');
			//						bd.NewValueEdit('customer_guid', a.customer.getValue('customer_guid'));
			//						for (var i=0;i<changed.length;i++) {
			//                            var key = changed[i];
			//							bd.NewValueEdit(key, a.customer.getValue(key));
			//						}
			//						bd.setCreateInfo("CREATEDDATE", "CREATEDBY", $E.staff.id);
			//						bd.setModifyInfo("MODIFIEDDATE", "MODIFIEDBY", $E.staff.id);
			//						upload.xmls.push({
			//							dbPool : '',
			//							xmlD: bd.getData()
			//						});
			//					}
			//				}

			// 提交upload数据
			upload.token = a.project.token;
			//console.log('Instance saveData:', upload);
			var exec = 0;
			$F.dataService('CacheDoAll', {
				data : upload,
				digest : a.project.digest,
				async : false,
				success : function(data) {
					//console.log('instance saved', data);
					exec = data.code;
					if (data.code < 0) {
						exec = 0;
						$F.err('Instance - save', '保存动态页面失败:' + data.message);
					}
				},
				fail : function() {
					exec = 0;
				}
			})
			return exec;
		},

		saveDyn2 : function() {
			const a = this, dataBound = new BoundData('InstanceSave');
			if (a.objective && a.workorder) {
				dataBound.addSub('objective', a.objective.getBoundData());
			}

			let hisTables = [], ignore = a.attributes['__IGNORE_TABC_SAVE'] || [];
			for (let tableName in a.data) {
				if (tableName === 'temp' || ignore.contains(tableName))
					continue;
				hisTables.push(tableName.toUpperCase());
			}
			if (hisTables.length > 0) {
				$F.dataService('SystemDo2', {
					data: $F.sqlbean2('System.tabc.hisflag', a.project.token,
						$E.dataService.saas ? a.project.getParam('CFGDBP') : a.dbPool, [hisTables]),
					async: false,
					digest: a.project.digest,
					success: function(data) {
						var beans = $F.makeJsonArray(data.value[0]);
						hisTables = [];
						beans.forEach(function(item, x) {
							if (item.INSERTHIS == 1)
								hisTables.push(item.PROPERTYGRPNAME.toUpperCase());
						})
					}
				});
			}

			//console.log('TABC HisFlag: ', hisTables);

			let sub,
				his,
				property,
				bdType;
			for (let tableName in a.data) {
				if (tableName === 'temp' || ignore.contains(tableName))
					continue;
					// tabc表数据收集

				sub = new BoundData('TABC' + (tableName + $E.staff.epidSql).toUpperCase());
				sub.setKey('RELATION_ID', a.objective.getId());
				for (let field in a.data[tableName]) {
					property = a.getProperty(tableName + '.' + field);
					if (property) {
						if (property.t == 1 || property.t == 5)
							bdType = 1;
						else if (property.t == 2)
							bdType = 2;
						else
							bdType = 0;
						sub.setValue(field, a.data[tableName][field], bdType);
					}
				}
				if (a.project.getParam("WODELO")) {
					let objectiveFields = ['MAP_ID', 'CURSTEP_ID_MAP', 'OBJECTIVESUBSTATUS', 'OBJECTIVESTATUS',
						'CUSTOMER_GUID', 'WORKORDERID', 'CATEGORY', 'WORKORDERTYPE1', 'WORKORDERTYPE2',
						'WORKORDERTYPE3', 'WORKORDERTYPE4', 'COMMENTS',
						'HANDLEROLE_ID', 'HANDLEGROUP_ID', 'HANDLEBY_ID'];
					let objectiveDateFields = ['EXPIREDDATE', 'FORECASTDATE'];
					objectiveFields.forEach(field => {
						sub.setValue(field, a.objective.getValue(field));
					});
					objectiveDateFields.forEach(field => {
						sub.setValue(field, a.objective.getValue(field), $CONST.BoundType.F_Date);
					});
					if (a.workorder.newFlag === 1) {
						sub.setValue('CREATEDBY', $E.staff.id);
						sub.setValue('CREATEDDATE', '##?#NOWDD#?##', $CONST.BoundType.F_Date);
					}
					sub.setValue('MODIFIEDDATE', '##?#NOWDD#?##', $CONST.BoundType.F_Date);
					sub.setValue('MODIFIEDBY', $E.staff.id);
					sub.setValue('OBJ_ROLEGROUP_ID', a.objective.getValue('ROLEGROUP_ID'));
					let elitetaskFields = ['OBFLAG', 'PRIORITY', 'EXPECTSTARTTIME', 'EXPECTENDTIME'];

					let task;
					if (a.workorder.operate === 'send' || a.workorder.operate === 'back') {
						task = a.workorder.operateTaskBoundData;
					} else if (a.workorder.operate === 'close' || a.workorder.operate === 'failedClose') {
						task = null;
					} else {
						task = a.workorder.taskBoundData;
					}
					if (task) {
						let elitetaskFields = [{
							name: 'OBFLAG',
							type: $CONST.BoundType.F_Int
						}, {
							name: 'PRIORITY'
						}, {
							name: 'EXPECTSTARTTIME',
							type: $CONST.BoundType.F_Date
						}, {
							name: 'EXPECTENDTIME',
							type: $CONST.BoundType.F_Date
						}];
						elitetaskFields.forEach(field => {
							sub.setValue(field.name, task.getValue(field.name), field.type);
						});
					}
				}
				dataBound.addSub(tableName, sub);

				if (hisTables.contains(tableName.toUpperCase())) {
					his = sub.copy('TABC' + (tableName + '_HIS' + $E.staff.epidSql).toUpperCase());
					his.notFindDupli = 1;
					his.setValue('HISGUID', a.data[tableName]['hisguid']);
					dataBound.addSub(tableName + '_his', his);
				}
			}
			// console.log(dataBound)

			// 整理xmldos
			for (let key in a.xmldos) {
				dataBound.addSub('SUB_' + key, a.xmldos[key].xmlD);
			}

			const result = $F.ajaxSync("SystemDo5", {
				token : a.project.token,
				dbPool : a.dbPool,
				xml : dataBound.getData()
			}, a.project.digest);
			return result.code;
		},

		/**
		 * 获取第一个tabc表的表名
		 * @return {string}
		 */
		fetchTabcName : function() {
			let ignore = this.attributes['__IGNORE_TABC_SAVE'] || [];
			for (let tableName in this.data) {
				if (tableName !== 'temp' && !ignore.contains(tableName)) {
					return tableName;
				}
			}
		},

		/**
		 * 仅仅保存当前页面objective
		 * @return {*}
		 */
		saveObjective : function() {
			var a = this,
				ret;
			var ox = a.objective.getXmlData();
			if (ox) {
				ret = $F.ajaxSync("SystemDo5", {
					token : a.project.token,
					dbPool : a.dbPool,
					xml : ox
				}, a.project.digest);
			}
			return ret;
		},

		closeWindow : function(e) {
			if (this.window) {
				if (e) { // 正常模式关闭窗口，执行相应命令
					Helper.masker.hide(this.window);
					this.window.remove();
					this.destroy();
				} else { // 直接关闭，不做任何处理
					$(".fancybox-close", this.window).trigger('click');
				}
			}
			else
				$F.err('Instance - closeWindow', '当前实例不在弹出式虚拟窗口中');
		},

		minWindow : function() {
			if (this.window)
				$(".fancybox-min", this.window).trigger('click');
			else
				$F.err('Instance - minWindow', '当前实例不在弹出式虚拟窗口中')
		},

		maxWindow : function() {
			if (this.window)
				$(".fancybox-max", this.window).trigger('click');
			else
				$F.err('Instance - maxWindow', '当前实例不在弹出式虚拟窗口中')
		},

		/**
		 * 向instance注册关闭TAB时的回调方法
		 * @method onDestroy
		 * @param callback {callback} 关闭TAB时触发实例的回调方法，允许注册多个
		 */
		onDestroy : function(callback) {
			var a = this;
			if (!a.attributes[$CONST.Instance.ON_DESTROY])
				a.attributes[$CONST.Instance.ON_DESTROY] = [];
			a.attributes[$CONST.Instance.ON_DESTROY].push(callback);
		},

		/**
		 * 销毁实例数据
		 * @method destroy
		 * @return instance 实例化对象
		 */
		destroy : function() {
			var a = this;
			$F.log('Instance - destroy', '销毁动态页面实例：' + a.id)
			// 注销键盘监听
			$E.unregisterHotkey(a);
			// 注销在project上的所有事件监听
			a.project.events.remove(a.id);
			// 清除所有定时事务
			a.clearAllTasks();

			if (a.attributes && a.attributes[$CONST.Instance.ON_DESTROY]) {
				$.each(a.attributes[$CONST.Instance.ON_DESTROY], function(i, callback) {
					try {
						callback();
					} catch (e) {
						$F.err('Instance - destroy', e);
					}
				});
			}

			// 调用所有组件destroy方法
			var allComponents = CtlUtils.findAllComponents(a.runtime);
			for (var key in allComponents) {
				allComponents[key].destroy();
			}
			a.destroyCtls(a.runtime.ctls);

			// 调用所有内嵌的实例
			$.each(a.embed, function(i, emb) {
				if ($.isFunction(emb.destroy)) {
					emb.destroy();
				}
			});
			a.embed = [];

			//调用所有plugins的close方法，并置空plugin
			$.each(a.plugins, function(i, plugin) {
				if (plugin && $.isFunction(plugin.close)) {
					plugin.close();
				}
			});
			a.plugins = [];

			$.each(a.wos, function(i, wo) {
				if (wo && $.isFunction(wo.clearInfo)) {
					wo.clearInfo();
				}
			});
			a.wos = [];

			delete a.events;
			a.runtime.shell = null;
			a.runtime = {};
			a.databinder.clear();
			a.data = {};
			a.flows = {};
			a.sqlQueue = {};
			a.sysdos = {};
			a.cachedos = {};
			a.xmldos = {};
			a.externalListener = {};
			a.bindCustomerProperties = {};
			a.bindObjectiveProperties = {};
			a.attributes = {};
			delete a.objective;
			delete a.parameters;
			delete a.tabCtrl;
			delete a.workorder;
			delete a.leftSideBar;
			delete a.rightSideBar;
			delete a.topSideBar;
			delete a.bottomSideBar;
		},

		/**
		 * 清空所有ctls
		 */
		destroyCtls : function(ctls) {
			$.each(ctls, function(i, ctl) {
				if ($.isFunction(ctl.destroy))
					ctl.destroy();

				if (ctl.box) {
					ctl.box.remove().html('');
					ctl.box = null;
				}
				if (ctl.dom) {
					ctl.dom.remove().html('');
					ctl.dom = null;
				}
				if (ctl.input) {
					try {
						if (ctl.input.remove) {
							ctl.input.remove().html('');
						} else {
							if (ctl.input.box) {
								ctl.input.box.remove().html('');
							}
							if (ctl.input.dropdown) {
								ctl.input.dropdown.remove().html('');
							}
							if (ctl.input.input) {
								ctl.input.input.remove().html('');
							}
							if (ctl.input.menuLayer) {
								ctl.input.menuLayer.remove().html('');
							}
							if (ctl.input.toggle) {
								ctl.input.toggle.remove().html('');
							}
						}
					} catch (e) {}
				}
				if (ctl.lb) {
					ctl.lb.remove().html('');
					ctl.lb = null;
				}
				if (ctl.form) {
					ctl.form.remove().html('');
					ctl.form = null;
				}
				if (ctl.ul) {
					ctl.ul.remove().html('');
					ctl.ul = null;
				}
				if (ctl.label && ctl.label.remove) {
					ctl.label.remove().html('');
					ctl.label = null;
				}
				if (ctl.icon && ctl.icon.remove) {
					ctl.icon.remove().html('');
					ctl.icon = null;
				}
				if (ctl.runtime && ctl.runtime.shell && ctl.runtime.shell.remove) {
					ctl.runtime.shell.remove().html('');
					ctl.runtime.shell = null;
				}
				Object.keys(ctl).forEach(key => {
					ctl[key] = null;
				});
				ctl = null;
			});
		},

		lockTab : function(state, mute) {
			var tab = this.project.tabFindByInstanceId(this.id);
			if (tab) {
				tab.data('closable', !state);
				if (state) {
					tab.find('.ui-icon-close').remove();
				} else {
					if (tab.find('>a .ui-icon-close').length == 0) {
						tab.find('>a').append('<span class="ui-icon ui-icon-close"></span>');

                        $('span.ui-icon-close', tab).on("click", function(e) {
                            e.preventDefault();
                            e.stopPropagation();
                            $(document).click();
                            // 注册TAB的关闭按钮
                            if (!$('#mainContainer').hasClass('locked')) {
                                let tab = $(this).closest("li");
                                if (tab.data('cacheable'))
                                    a.tabHide(tab);
                                else
                                    a.tabClose(tab);
                            }
                        });
                    }
				}
			} else {
				if (!mute) {
					$F.alert('异常：没有发现对应的框架TAB')
				}
			}
		},

		/**
		 * 激活tab时 active该tab页上所有图表控件
		 */
		activeChart : function() {
			var a = this,
				chartCtls = [];

			var getChartCtlFromRuntime = function(runtime) {
				// 当前runtime上的 chart-ctl
				for (var ctlId in runtime.ctls) {
					if (runtime.ctls[ctlId].type == 'CHART') {
						chartCtls.push(runtime.ctls[ctlId]);
					}
				}
				// 循环子runtime
				for (var childKey in runtime.children) {
					getChartCtlFromRuntime(runtime.children[childKey]);
				}
			}
			getChartCtlFromRuntime(a.runtime);

			$.each(chartCtls, function(index, chartCtl) {
				chartCtl.active(); // resize instance上的图表控件
			});
		},

		/**
		 * 激活此实例时候触发，比如tab页切换到此实例时候
		 */
		active : function(parameters) {
			if (this.struct.version === 'DYN2') {
				this.isActive = true;
				if (parameters) {
					// parameters有值时，需要额外触发reset命令
					this.doCmdIncludeComponents('reset', parameters, this.runtime, this.id, true);
				} else {
					// parameters未定义，意味着是通过project tab切换引起的触发
					this.doCmdIncludeComponents('active', parameters, this.runtime, this.id, true);
					// 激活(resize)当前tab页上的chart
					this.activeChart();
				}

				// 页面激活时候，触发其他页面的inactive事件
				const nav = this.project.getNavs();
				for (var i = 0; i < nav.length; i++) {
					let instance = $(nav[i]).data("instance");
					if (instance && instance.id !== this.id && instance.inactive) {
						instance.inactive();
					}
				}
			}

			return this;
		},

		// 页面进入不活跃状态事件(当某个页面激活后，其他页面就会进入不活跃状态)
		inactive: function(parameters) {
			if (this.struct.version === 'DYN2') {
				// 查看task是否有需要暂停的
				this.isActive = false;
				this.doCmdIncludeComponents('inactive', parameters, this.runtime, this.id, true);
			}
			return this;
		},

		/**
		 * 刷新当前实例，该方法将重新创建一个新的实例，并将原实例销毁
		 */
		reset : function() {
			var a = this, newInstance = new Instance(a.project, a.struct, a.otId, a.oId, a.parameters, a.inner, a.type);
			newInstance.addinId = a.addinId;
			var tab = newInstance.tabCtrl = a.tabCtrl;
			tab.data("instance", newInstance);

			a.destroy();
			var container = tab.data("container");
			container.empty().html('');
			$D.live(newInstance, container);
			return newInstance;
		}
	};

	return {
		create : function(project, struct, otId, oId, parameters, inner, type) {
			return new Instance(project, struct, otId, oId, parameters, inner, type);
		}
	}
})
